/*
 * Este programa es software libre; usted puede redistribuirlo y/o modificarlo bajo los terminos
 * de la licencia "GNU General Public License" publicada por la Fundacion "Free Software Foundation".
 * Este programa se distribuye con la esperanza de que pueda ser util, pero SIN NINGUNA GARANTIA;
 * vea la licencia "GNU General Public License" para obtener mas informacion.
 */
package meta.proyecto.base;

import adalid.commons.properties.PropertiesHandler;
import adalid.commons.properties.SortedProperties;
import adalid.commons.util.StrUtils;
import adalid.core.Operation;
import adalid.core.Project;
import adalid.core.annotations.ProjectModule;
import adalid.core.enums.Kleenean;
import adalid.core.enums.SecurityRealmType;
import adalid.core.interfaces.Artifact;
import adalid.core.interfaces.Entity;
import adalid.core.interfaces.Parameter;
import adalid.core.interfaces.Property;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeSet;
import meta.proyecto.comun.EntidadesComunes;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;

/**
 * @author Jorge Campins
 */
public abstract class ProyectoBase extends Project {

    protected static final String PLATAFORMA_BASE = "jee1ap101";

    protected static final String PLATAFORMA_NETBEANS_POSTGRESQL_GLASSFISH = "jee1af101";

    protected static final String PLATAFORMA_NETBEANS_POSTGRESQL_JBOSS = "jee1af102";

    protected static final String PLATAFORMA_ECLIPSE_POSTGRESQL_GLASSFISH = "jee1af201";

    protected static final String PLATAFORMA_ECLIPSE_POSTGRESQL_JBOSS = "jee1af202";

    public static String getEsquemaEntidadesComunes() {
//      return getString("adalid.schema");
        return "";
    }

    public ProyectoBase() {
        super();
        setUserEntityClass(meta.entidad.comun.control.acceso.Usuario.class);
        getSingularPlatforms().add(PLATAFORMA_BASE);
    }

    protected ModuloConsulta consulta;

    protected ModuloProcesamiento procesamiento;

    protected ModuloRegistro registro;

    protected ModuloConsultaTarea tarea;

    protected ModuloRegistroFiltro filtro;

    protected ModuloRegistroPrueba prueba;

    @ProjectModule(menu = Kleenean.FALSE, role = Kleenean.FALSE)
    protected EntidadesComunes commons;

    @Override
    public boolean analyze() {
        boolean analyzed = super.analyze();
        if (analyzed) {
//          loadDictionary();
            initDictionary();
            buildDictionary();
//          storeDictionary();
        }
        return analyzed;
    }

    // <editor-fold defaultstate="collapsed" desc="static fields">
    private static final String BASE_NAME = ProyectoBase.class.getName();

    private static final ResourceBundle RB = ResourceBundle.getBundle(BASE_NAME);

    private static final String DIR = System.getProperties().getProperty("user.dir");

    private static final String SEP = System.getProperties().getProperty("file.separator");

    private static final String PROPERTIES_SUFFIX = ".properties";

    private static final String DICTIONARY_DIR = DIR + SEP + "dictionary";

    private static final String DICTIONARY_NEXT_ID_KEY = "$next$id$";

    private static final String ENTITIES_DICTIONARY = DICTIONARY_DIR + SEP + "entities" + PROPERTIES_SUFFIX;

    private static final String OPERATIONS_DICTIONARY = DICTIONARY_DIR + SEP + "operations" + PROPERTIES_SUFFIX;

    private static final String PARAMETERS_DICTIONARY = DICTIONARY_DIR + SEP + "parameters" + PROPERTIES_SUFFIX;

    private static final Locale DEFAULT_LOCALE = Locale.forLanguageTag(getString("locale.tag"));

    private static final List<String> SQL_FOLDERS_LIST = getStringList("sql.folders");
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="static fields' public getters">
    public static Locale getDefaultLocale() {
        return DEFAULT_LOCALE;
    }

    public static List<String> getSqlFoldersList() {
        return SQL_FOLDERS_LIST;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="static fields' private getters">
    private static List<String> getStringList(String key) {
        String string = getString(key);
        if (string == null) {
            return null;
        }
        String[] strings = StringUtils.split(string, ", ");
        return Arrays.asList(strings);
    }

    private static String getString(String key) {
        return RB.getString(key);
    }
    // </editor-fold>

    private Properties _entitiesDictionary;

    private Properties _operationsDictionary;

    private Properties _parametersDictionary;

    private String _baseFolderName;

    private String _databaseName;

    private String _rootFolderName;

    private String _rootPackageName;

    private String _securityRealmName;

    private String _roleBasedAccessControllerName;

    private SecurityRealmType _securityRealmType = SecurityRealmType.JDBC;

    /**
     * @return the entities dictionary
     */
    public Properties getEntitiesDictionary() {
        return _entitiesDictionary;
    }

    /**
     * @return the operations dictionary
     */
    public Properties getOperationsDictionary() {
        return _operationsDictionary;
    }

    /**
     * @return the parameters dictionary
     */
    public Properties getParametersDictionary() {
        return _parametersDictionary;
    }

    /**
     * @return the entity keys
     */
    public Set<String> getEntityKeys() {
        Set<String> set = new TreeSet<>();
        set.addAll(getEntitiesMap().keySet());
        return set;
    }

    /**
     * @return the operation keys
     */
    public Set<String> getOperationKeys() {
        Set<String> set = new TreeSet<>();
        set.addAll(getDefaultCrudOperationKeys());
        set.addAll(getUserDefinedOperationKeys());
        return set;
    }

    /**
     * @return the CRUD operation keys
     */
    public Set<String> getDefaultCrudOperationKeys() {
        Set<String> set = new TreeSet<>();
        List<Entity> entities = getEntitiesList();
        String[] operations = Operation.getCrudOperationKeys();
        String simpleName;
        for (Entity entity : entities) {
            simpleName = entity.getDataType().getSimpleName();
            for (String name : operations) {
                set.add(simpleName + "." + name);
            }
        }
        return set;
    }

    /**
     * @return the user-defined operation keys
     */
    public Set<String> getUserDefinedOperationKeys() {
        Set<String> set = new TreeSet<>();
        List<Entity> entities = getEntitiesList();
        String simpleName;
        for (Entity entity : entities) {
            simpleName = entity.getDataType().getSimpleName();
            for (Operation operation : entity.getOperationsList()) {
                set.add(simpleName + "." + operation.getName());
            }
        }
        return set;
    }

    /**
     * @return the parameter keys
     */
    public Set<String> getParameterKeys() {
        Set<String> set = new TreeSet<>();
        List<Entity> entities = getEntitiesList();
        String simpleName;
        for (Entity entity : entities) {
            simpleName = entity.getDataType().getSimpleName();
            for (Property property : entity.getPropertiesList()) {
                set.add(simpleName + "." + property.getName());
            }
            for (Operation operation : entity.getOperationsList()) {
                for (Parameter parameter : operation.getParametersList()) {
                    set.add(simpleName + "." + operation.getName() + "." + parameter.getName());
                }
            }
        }
        return set;
    }

    /**
     * @return the entity number
     */
    public String getEntityNumber(Entity entity) {
        if (entity == null) {
            return "?";
        }
//      String key = entity.getName();
        String key = entity.getDataType().getSimpleName();
        return getEntityNumber(key);
    }

    /**
     * @return the entity number
     */
    public String getEntityNumber(String key) {
        if (StringUtils.isBlank(key)) {
            return "?";
        }
        return _entitiesDictionary.getProperty(key, "?");
    }

    /**
     * @return the operation number
     */
    public String getOperationNumber(Operation operation) {
        if (operation == null) {
            return "?";
        }
        String name = operation.getName();
        Entity declaringEntity = operation.getDeclaringEntity();
        return getOperationNumber(name, declaringEntity);
    }

    /**
     * @return the operation number
     */
    public String getOperationNumber(String name, Entity declaringEntity) {
        if (StringUtils.isBlank(name) || declaringEntity == null) {
            return "?";
        }
        String simpleName = declaringEntity.getDataType().getSimpleName();
        String key = simpleName + "." + name;
        return getOperationNumber(key);
    }

    /**
     * @return the operation number
     */
    public String getOperationNumber(String key) {
        if (StringUtils.isBlank(key)) {
            return "?";
        }
        return _operationsDictionary.getProperty(key, "?");
    }

    /**
     * @return the parameter number
     */
    public String getParameterNumber(Artifact artifact) {
        if (artifact == null) {
            return "?";
        }
        Artifact declaringArtifact = artifact.getDeclaringArtifact();
        if (artifact instanceof Property && declaringArtifact instanceof Entity) {
            String name = artifact.getName();
            Entity declaringEntity = (Entity) declaringArtifact;
            return getParameterNumber(name, declaringEntity);
        }
        if (artifact instanceof Parameter && declaringArtifact instanceof Operation) {
            String name = artifact.getName();
            Operation declaringOperation = (Operation) declaringArtifact;
            return getParameterNumber(name, declaringOperation);
        }
        return "?";
    }

    /**
     * @return the parameter number
     */
    public String getParameterNumber(String name, Entity declaringEntity) {
        if (StringUtils.isBlank(name) || declaringEntity == null) {
            return "?";
        }
        String simpleName = declaringEntity.getDataType().getSimpleName();
        String key = simpleName + "." + name;
        return getParameterNumber(key);
    }

    /**
     * @return the parameter number
     */
    public String getParameterNumber(String name, Operation declaringOperation) {
        if (StringUtils.isBlank(name) || declaringOperation == null) {
            return "?";
        }
        Entity declaringEntity = declaringOperation.getDeclaringEntity();
        if (declaringEntity == null) {
            return "?";
        }
        String simpleName = declaringEntity.getDataType().getSimpleName();
        String key = simpleName + "." + declaringOperation.getName() + "." + name;
        return getParameterNumber(key);
    }

    /**
     * @return the parameter number
     */
    public String getParameterNumber(String key) {
        if (StringUtils.isBlank(key)) {
            return "?";
        }
        return _parametersDictionary.getProperty(key, "?");
    }

    private void loadDictionary() {
        _entitiesDictionary = PropertiesHandler.loadProperties(ENTITIES_DICTIONARY, true, Level.WARN);
        _operationsDictionary = PropertiesHandler.loadProperties(OPERATIONS_DICTIONARY, true, Level.WARN);
        _parametersDictionary = PropertiesHandler.loadProperties(PARAMETERS_DICTIONARY, true, Level.WARN);
    }

    private void initDictionary() {
        _entitiesDictionary = new SortedProperties();
        _operationsDictionary = new SortedProperties();
        _parametersDictionary = new SortedProperties();
    }

    private void buildDictionary() {
        addKeys(_entitiesDictionary, getEntityKeys());
        addKeys(_operationsDictionary, getOperationKeys());
        addKeys(_parametersDictionary, getParameterKeys());
    }

    private void addKeys(Properties properties, Set<String> keys) {
        String id$ = properties.getProperty(DICTIONARY_NEXT_ID_KEY);
        long id = StringUtils.isNumeric(id$) ? new Long(id$) : 1;
        for (String key : keys) {
            if (properties.containsKey(key)) {
            } else {
                properties.setProperty(key, "" + id++);
            }
        }
        properties.setProperty(DICTIONARY_NEXT_ID_KEY, "" + id);
    }

    private void storeDictionary() {
        PropertiesHandler.storeProperties(_entitiesDictionary, ENTITIES_DICTIONARY, getName());
        PropertiesHandler.storeProperties(_operationsDictionary, OPERATIONS_DICTIONARY, getName());
        PropertiesHandler.storeProperties(_parametersDictionary, PARAMETERS_DICTIONARY, getName());
    }

    /**
     * @return the base folder name
     */
    public String getBaseFolderName() {
        return StringUtils.defaultIfBlank(_baseFolderName, getDefaultFolderName());
    }

    /**
     * @param baseFolderName the base folder name to set
     */
    public void setBaseFolderName(String baseFolderName) {
        _baseFolderName = baseFolderName;
    }

    /**
     * @return the database name
     */
    public String getDatabaseName() {
        return StringUtils.defaultIfBlank(_databaseName, getDefaultDatabaseName());
    }

    /**
     * @param databaseName the database name to set
     */
    public void setDatabaseName(String databaseName) {
        _databaseName = databaseName;
    }

    /**
     * @return the root folder name
     */
    public String getRootFolderName() {
        return StringUtils.defaultIfBlank(_rootFolderName, getDefaultFolderName());
    }

    /**
     * @param rootFolderName the root folder name to set
     */
    public void setRootFolderName(String rootFolderName) {
        _rootFolderName = rootFolderName;
    }

    /**
     * @return the root package name
     */
    public String getRootPackageName() {
        return StringUtils.defaultIfBlank(_rootPackageName, getDefaultPackageName());
    }

    /**
     * @param rootPackageName the root package name to set
     */
    public void setRootPackageName(String rootPackageName) {
        _rootPackageName = rootPackageName;
    }

    /**
     * @return the security realm name
     */
    public String getSecurityRealmName() {
        return StringUtils.defaultIfBlank(_securityRealmName, getDefaultSecurityRealmName());
    }

    /**
     * @param securityRealmName the security realm name to set
     */
    public void setSecurityRealmName(String securityRealmName) {
        _securityRealmName = securityRealmName;
    }

    /**
     * @return the role-based-access-controller (RBAC) name
     */
    public String getRoleBasedAccessControllerName() {
        return StringUtils.defaultIfBlank(_roleBasedAccessControllerName, "$LOWER_CASE_CODE");
    }

    /**
     * @param roleBasedAccessControllerName the role-based-access-controller (RBAC) name to set
     */
    public void setRoleBasedAccessControllerName(String roleBasedAccessControllerName) {
        _roleBasedAccessControllerName = roleBasedAccessControllerName;
    }

    /**
     * @return the security realm type
     */
    public SecurityRealmType getSecurityRealmType() {
        return _securityRealmType;
    }

    /**
     * @param securityRealmType the security realm type to set
     */
    public void setSecurityRealmType(SecurityRealmType securityRealmType) {
        _securityRealmType = securityRealmType;
    }

    protected String getDefaultDatabaseName() {
        String string = StringUtils.defaultIfBlank(getAlias(), getName());
        return StrUtils.getLowerCaseIdentifier(string, '-');
    }

    protected String getDefaultFolderName() {
        String string = StringUtils.defaultIfBlank(getAlias(), getName());
        return StrUtils.getLowerCaseIdentifier(string, '-');
    }

    protected String getDefaultPackageName() {
        String string = StringUtils.defaultIfBlank(getAlias(), getName());
        return StrUtils.getLowerCaseIdentifier(string, '.');
    }

    protected String getDefaultSecurityRealmName() {
        String string = StringUtils.defaultIfBlank(getAlias(), getName());
        String prefix = StrUtils.getLowerCaseIdentifier(string, '-');
        String srtype = _securityRealmType == null ? "jdbc" : _securityRealmType.name().toLowerCase();
        String suffix = "realm";
        return prefix + "-" + srtype + "-" + suffix;
    }

}
